package com.inspectortime.webapp;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

import com.inspectortime.domain.EmailTemplate;
import com.inspectortime.domain.Inspection;
import com.inspectortime.domain.InspectorCompany;
import com.inspectortime.domain.OptionalService;
import com.inspectortime.domain.RealEstateAgent;
import com.inspectortime.domain.pricing.PriceAdjusterException;
import com.inspectortime.domain.type.InspectionStatus;
import com.inspectortime.domain.type.State;
import com.inspectortime.mail.EmailMessageUtils;
import com.inspectortime.mail.service.AbstractMailMessage;
import com.inspectortime.mail.service.DynamicMailMessage;
import com.inspectortime.mail.service.MailService;
import com.inspectortime.mail.service.StaticMailMessage;
import com.inspectortime.mail.service.StaticMailMessageTemplateNames;
import com.inspectortime.repository.Factory;
import com.inspectortime.repository.Repository;
import com.inspectortime.util.SpringValidatorUtils;
import com.inspectortime.webapp.inspector.EditReferringRealtorFormValidator;
import com.inspectortime.webapp.util.CustomStateEditor;
import com.inspectortime.webapp.util.DomainUtils;
import com.inspectortime.webapp.util.HttpSessionUtils;
import com.inspectortime.webapp.util.RequestPathUtils;

/**
 * 
 * @author mkvalsvik
 * 
 */
public class InspectionExtraInformationController extends
		SimpleFormController {

	private static Log log = LogFactory
			.getLog(InspectionExtraInformationController.class);
	private Factory factory;
	private Repository repository;
	private MailService mailService;
	
	public void setMailService(MailService mailService) {
		this.mailService = mailService;
	}

	private EditReferringRealtorFormValidator realtorFormValidator;

	public InspectionExtraInformationController() {
		super();
	}
	
	protected Object formBackingObject(HttpServletRequest request) throws Exception {
		InspectionCriteria inspectionCriteria = new InspectionCriteria();
		inspectionCriteria.setInspection(HttpSessionUtils.retrieveWizardInspection(request, repository));
		
		//prepare empty data objects for the upcoming page
		inspectionCriteria.setReferringRealEstateAgent(factory.createRealEstateAgent());
		return inspectionCriteria;
	}

	protected ModelAndView onSubmit(HttpServletRequest request,
			HttpServletResponse response, Object command, BindException errors)
			throws Exception {

		InspectionCriteria inspectionCriteria = (InspectionCriteria) command;
		Inspection inspection = inspectionCriteria.getInspection();
		String inspectorCompanyShortName = RequestPathUtils.extractInspectorCompanyShortNameFromPath(request);
		InspectorCompany inspectorCompany = repository.findInspectorCompanyByShortName(inspectorCompanyShortName);

		// Radio listSelection requires drop-down selection
		if (inspectionCriteria.getReferringRealtorInputType().equals("listSelection")) {
			RealEstateAgent realtor = null;

			// the normal realtor dropdown for non-javascript enabled browsers
			if (StringUtils.hasText(inspectionCriteria.getReferringAgentId())) {
				inspection.setReferringAgent(repository.loadRealEstateAgent(inspectionCriteria.getReferringAgentId()));
			} else {
				// the auto-complete input field
				inspection.setReferringAgent(DomainUtils.getExistingRealtorByAutoCompletionName(inspectionCriteria.getSelectedRealtor(), 
																								inspection.getInspector().getCompany().getReferringAgents()));
			}
		} else if (inspectionCriteria.getReferringRealtorInputType().equals("enteredDetails")) {

			// Add agent to inspector's list
			inspectorCompany.getReferringAgents().add(inspectionCriteria.getReferringRealEstateAgent());
			inspection.setReferringAgent(inspectionCriteria.getReferringRealEstateAgent());
			// inspectorCompany.save();
			inspection.getInspector().setCompany(inspectorCompany);
			
		} else if (inspectionCriteria.getReferringRealtorInputType().equals("noRef")) {
			inspection.setReferringAgent(null);
		}

		ModelAndView mv = new ModelAndView(this.getSuccessView());

		// Re-calculate inspection fee in case it changed
		double oldFee = inspection.getInspectionFee();
		inspection.setInspectionFee(inspectorCompany.calculateInspectionFee(inspection));
		if (oldFee != inspection.getInspectionFee()) {
			mv = new ModelAndView(this.getSuccessView()+"?feeChange=true");
			String msg = "Added additional services. New fee: " + inspection.getInspectionFee();
			log.info(msg);
		}
		
		inspection.setInspectionStatus(InspectionStatus.SCHEDULED);
		inspection.save();
		log.info("Confirmed order: " + inspection.getInspector().getCompany().getShortName() + " for inspector " + inspection.getInspector().getEmail() + " at " + inspection.getInspectionTime());
		
		
		//send confirmation emails
		try {
			AbstractMailMessage message = EmailMessageUtils.prepareBuyerConfirmationMessage(inspection, request);
			this.mailService.send(message);			
	
			AbstractMailMessage message2 = EmailMessageUtils.prepareInspectorConfirmationMessage(inspection, request);
			this.mailService.send(message2);
			
			if (inspection.getReferringAgent() != null && StringUtils.hasText(inspection.getReferringAgent().getEmail())) {
				AbstractMailMessage message3 = EmailMessageUtils.prepareRealtorThankYouMessage(inspection, request);
				this.mailService.send(message3);
			}
		} catch (Exception e) {
			//Let's not show an error page if the email fails. Just log it and move on
			log.error("Exception thrown trying to send email for inspection id: " + inspection.getId(), e);
		}
		
		return mv;
	}

	protected void onBindAndValidate(HttpServletRequest request,
			Object command, BindException errors) throws Exception {

		InspectionCriteria inspectionCriteria = (InspectionCriteria) command;
		Inspection inspection = inspectionCriteria.getInspection();

		// Manually bind the optionalService.id.X checkboxes by index
		String inspectorCompanyShortName = RequestPathUtils.extractInspectorCompanyShortNameFromPath(request);
		InspectorCompany inspectorCompany = repository.findInspectorCompanyByShortName(inspectorCompanyShortName);

		for (int i = 0; i < inspectorCompany.getOptionalServices().size(); i++) {
			String param2 = request.getParameter("optionalService.id." + i);
			OptionalService service = inspectorCompany.getOptionalServices().get(i);
			if (service == null) {
				throw new RuntimeException("Optional service for company "
						+ inspectorCompany.getShortName() + " at index "
						+ i + " not found, this should not happen.");
			}
			if (param2 != null && param2.equals("on")) {
				if (!inspection.getOptionalServices().contains(service)) {
					inspection.getOptionalServices().add(service);
				}
			} else {
				
				for (int j = 0; j < inspection.getOptionalServices().size(); j++) {
					String id = (String) inspection.getOptionalServices().get(j).getId();
					String name = inspection.getOptionalServices().get(j).getTitle();
				}
				// Remove
				if (inspection.getOptionalServices().contains(service)) {
					inspection.getOptionalServices().remove(service);
				}
			}
		}

		// Calculating the fee will trigger additional validation by the
		// PriceAdjuster implementations, and any exceptions should be shown to
		// user
		try {
			inspectorCompany.calculateInspectionFee(inspection);
		} catch (PriceAdjusterException e) {
			// TODO: Externalize/create codes for each exception
			errors.reject("dummy.price.error.code", e.getMessage());
		}


		// Explictly handle realtor validation
		// First, no radio selection made
		if (!StringUtils.hasText(inspectionCriteria.getReferringRealtorInputType())) {
			errors.rejectValue("referringRealtorInputType", "required");
			
		} else {

			// Radio listSelection requires drop-down selection (for
			// non-javascript) or a selection
			// from the autocomplete dropdown
			if (inspectionCriteria.getReferringRealtorInputType().equals("listSelection")) {
				if (!StringUtils.hasText(inspectionCriteria.getReferringAgentId())
						&& !StringUtils.hasText(inspectionCriteria.getSelectedRealtor())) {
					errors.rejectValue("selectedRealtor", "required");
					
				} else if (StringUtils.hasText(inspectionCriteria.getSelectedRealtor())) {
					RealEstateAgent realtor = DomainUtils.getExistingRealtorByAutoCompletionName(inspectionCriteria.getSelectedRealtor(), 
							 inspection.getInspector().getCompany().getReferringAgents()); 
					if (realtor == null) {
						errors.rejectValue("selectedRealtor", "notFound");
					}
				}

			} else if  (inspectionCriteria.getReferringRealtorInputType().equals("enteredDetails")) {
				// Radio enteredDetails requires field-level validation
				errors.pushNestedPath("referringRealEstateAgent");
				this.realtorFormValidator.validate(inspectionCriteria.getReferringRealEstateAgent(), errors);
				errors.popNestedPath();
			}
		}
			
		// For debugging only: Log all validation errors
		SpringValidatorUtils.logValidationErrors(log, errors);
	}

	protected Map referenceData(HttpServletRequest request) throws Exception {
		Map<String, Object> data = new HashMap<String, Object>();
		// Look up realtors
		String inspectorCompanyShortName = RequestPathUtils
				.extractInspectorCompanyShortNameFromPath(request);
		if (inspectorCompanyShortName != null) {
			InspectorCompany inspectorCompany = repository
					.findInspectorCompanyByShortName(inspectorCompanyShortName);
			if (inspectorCompany == null) {
				throw new RuntimeException("Inspector company "
						+ inspectorCompanyShortName + " not found in database.");
			}
			Collection referringRealtors = inspectorCompany
					.getReferringAgents();
			data.put("referringRealtors", referringRealtors);
			data.put("inspectorCompany", inspectorCompany);
		}
		return data;
	}
	
	public void setRealtorFormValidator(
			EditReferringRealtorFormValidator realtorFormValidator) {
		this.realtorFormValidator = realtorFormValidator;
	}

	protected void initBinder(HttpServletRequest request,
			ServletRequestDataBinder binder) throws Exception {
		// Feb 4, 2008 8:00:00 AM
		DateFormat df = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a");
		df.setLenient(true);
		CustomDateEditor editor = new CustomDateEditor(df, false);
		binder.registerCustomEditor(Date.class, editor);
		binder.registerCustomEditor(State.class, new CustomStateEditor());
	}
	
	public void setFactory(Factory factory) {
		this.factory = factory;
	}

	public void setRepository(Repository repository) {
		this.repository = repository;
	}

}
